import 'dart:isolate';

import 'package:simulator/core/mcu.dart';
import 'package:simulator/core/module.dart';
import 'package:simulator/core/ram_rom.dart';
import 'package:simulator/core/regisiter.dart';
import 'package:simulator/core/result.dart';
import 'package:simulator/utils/constant.dart';
import 'package:simulator/utils/log.dart';
import 'package:simulator/utils/options.dart';
import 'package:simulator/utils/rx_util.dart';
import 'package:simulator/utils/utils.dart';

///模拟器管理类
///用于初始化环境
class Simulator {
  static final Simulator _instance = Simulator._internal();
  Simulator._internal();

  static Simulator getInstance() => _instance;

  late final MCU mcu;

  ///Host 端接收端口
  late ReceivePort _receivePort;

  ///UI 层端口
  ///todo 暂时放在这里
  late ReceivePort _uiPort;

  ///Host 端发送端口
  SendPort? _port;

  void sendToModule(Result data) => _port?.send(data);

  MCUState state = MCUState.unkown;

  ///被监听的地址
  final List<int> debugAddr = List.empty(growable: true);

  ///寄存器变化分发函数
  ///需要判断是本地分发还是线程分发
  _dispatch(data) {
    data as RegisiterUpdate;
    for (var a in mcu.module) {
      for (var b in a.registers) {
        if (b.addr == data.addr) {
          if (a.sync_) {
            Rx.getInstance().push(b.addr.toString(), data: data);
          } else {
            sendToModule(Result(type: ResultType.regisiter, update: data));
          }
        }
      }
    }
  }

  ///模拟器初始化
  Future<void> init({MCUState? stat}) async {
    state = MCUState.init;
    final List<Module> module = List.empty(growable: true);
    mcu = MCU.getConfig(Options.cmds);
    _receivePort = ReceivePort();
    _uiPort = ReceivePort();
    _receivePort.listen((e) {
      assert(e is Result, 'Iso must has Result data type: $e');
      e as Result;
      print('object:${e.type.name}');
      switch (e.type) {
        case ResultType.args:
          _port = e.args!.port;
          break;
        case ResultType.regisiter:

          ///来自模块的寄存器变化同步
          for (var a in mcu.module) {
            for (var b in a.registers) {
              if (b.addr == e.update!.addr) {
                b.valueNot = e.update!.newValue;
              }
            }
          }
          break;
        case ResultType.interrupt:
        case ResultType.exit:
        case ResultType.exception:
          LogPrint.logWarn('get exception $e');
        case ResultType.unkonw:
          LogPrint.logWarn('get unkonw cmd $e');
      }
    }, onDone: () {
      LogPrint.logErr('root listen Done');
    }, onError: (e) {
      LogPrint.logErr('root get error listen: $e');
    });
    RomAndRam.getInstance().loadFirmware(Options.cmds);

    ///开始监听寄存器变化
    Rx.getInstance().subscribe(signRegisiter, _dispatch);
    Utils.getInstance()
        .addShutdownHook(() => Rx.getInstance().unSubscribe(signRegisiter));

    ///启动模块
    ///同线程模块和不同线程模块
    for (var element in mcu.module) {
      if (element.sync_) {
        Utils.getInstance().addShutdownHook(element.exit);
        element.init();
        element.exec();
      } else {
        module.add(element);
      }
    }

    if (module.isNotEmpty) {
      ///启动线程去处理
      ///[Module.loop]为什么onError和onExit不生效todo ???
      Isolate.spawn(
        Module.loop,
        Result(
            type: ResultType.args,
            args: Args(
                port: _receivePort.sendPort,
                module: module,
                ui: _uiPort.sendPort)),
        debugName: 'module',
      );

      ///退出的时候会发一个null,应该是入口没有配置exit信息
      // onError: port.sendPort,
      // onExit: port.sendPort);
      /// 等待通信端口建立连接
      while (_port == null) {
        await Future.delayed(Duration.zero);
      }
    }

    state = stat ?? MCUState.running;
  }

  ///判断各种各样的条件
  ///1. 是否暂停,休眠,关机灯操作
  Future<int> mainLoopShouldExit() async {
    ///把监听数据发到UI去
    if (debugAddr.isNotEmpty) {
      final List<Regisiter> dgb = List.generate(debugAddr.length, (index) {
        final addr = debugAddr[index];
        for (var a in mcu.module) {
          for (var b in a.registers) {
            if (b.addr == addr) return b;
          }
        }
        return Regisiter(addr: addr);
      });
      _uiPort.sendPort.send(dgb);
    }
    if (state == MCUState.debug) {
      while (state == MCUState.debug) {
        await Future.delayed(Duration.zero);
      }
      if (state == MCUState.next) {
        state = MCUState.debug;
      }
    }
    if (state == MCUState.exit) {
      sendToModule(const Result(type: ResultType.exit));
      return 1;
    }
    return 0;
  }
}
